/**
    @project {
        "templateId": "org.helgoboss.custom",
        "version": "0.1-SNAPSHOT",
        "dependencies": [
            "org.scala-lang:scala-library:2.9.0-1",
            "org.scala-lang:scala-swing:2.9.0-1",
            "org.helgoboss:obr-facade:0.3-SNAPSHOT",
            "org.helgoboss:scala-additions:0.1-SNAPSHOT",
            "org.helgoboss:scala-swing-additions:0.2-SNAPSHOT"
        ]
    }
*/

package org.helgoboss.resource_manager_view

import scala.swing._
import scala.swing.event._
import org.osgi.service.obr.Repository
import javax.swing.event.{DocumentListener, DocumentEvent, TableModelListener, TableModelEvent}
import javax.swing.UIManager
import javax.swing.table.TableModel
import org.helgoboss.scala_swing_additions.{MigPanel, ProgressView}
import org.osgi.framework.{Bundle, Version}
import java.awt.Image
import org.helgoboss.obr_facade._

abstract class ResourceManagerView {
    def icon: image
    def title: String
    def progressViewTitle: String

    private lazy val resourceTable: Table = {
        val table = new Table {
            focusable = true
            
            listenTo(selection, keys)
            
            reactions += {
                case KeyPressed(_, Key.I, _, _) =>
                    installSelectedResource
                    
                case KeyPressed(_, Key.U, _, _) =>
                    uninstallSelectedResource
                    
                case KeyPressed(_, Key.R, _, _) =>
                    refreshSelectedResource
                    
                case KeyPressed(_, Key.S, _, _) =>
                    resourceTextField.requestFocus
                    
                case TableRowsSelected(_, _, _) =>
                    updateButtons
            }
            
            model = resourceTableModel
        }
        /* Set column widths */
        val columnModel = table.peer.getColumnModel
        columnModel.getColumn(0).setPreferredWidth(300)
        columnModel.getColumn(1).setPreferredWidth(100)
        columnModel.getColumn(2).setPreferredWidth(100)
        columnModel.getColumn(3).setPreferredWidth(200)
        columnModel.getColumn(4).setMaxWidth(100)
        
        table
    }
    
    object resourceTableModel extends TableModel {
        private val listeners = new collection.mutable.HashSet[TableModelListener]
        
        def updateData {
            listeners.foreach(_.tableChanged(new TableModelEvent(this)))
        }
        
        def addTableModelListener(listener: TableModelListener) {
            listeners += listener
        }
        
        def removeTableModelListener(listener: TableModelListener) {
            listeners -= listener
        }
        
        def getColumnClass(columnIndex: Int) = {    
            columnIndex match {
                case x if 0 until 4 contains x => classOf[String]
                case 5 => classOf[Boolean]
            }
        }
        
        def getColumnCount = 6
        
        def getColumnName(columnIndex: Int) = {
            columnIndex match {
                case 0 => "Symbolic Name"
                case 1 => "Version"
                case 2 => "Categories"
                case 3 => "Presentation Name"
                case 4 => "State"
                case 5 => "Repository"
            }
        }
        
        def getRowCount = resources.size
        
        def getValueAt(rowIndex: Int, columnIndex: Int) = {
            val resource = resources(rowIndex)
            columnIndex match {
                case 0 => resource.symbolicName
                case 1 => resource.version.toString
                case 2 => resource.categories.mkString(", ")
                case 3 => resource.presentationName
                case 4 => resource.state.displayName
                case 5 => 
                    resource.obrResource match {
                        case Some(_) => 
                            /* TODO Activate as soon as getRepository method is implemented */
                            //~ resource.getRepository.getURL.toString
                            "Unknown"
                        case None => "Not available"
                    }
            }
        }
        
        def isCellEditable(rowIndex: Int, columnIndex: Int) = false
        
        def setValueAt(value: AnyRef, rowIndex: Int, columnIndex: Int) {
        }
    }
    
    private lazy val resourceTextField: TextField = new TextField(50) {
        focusable = true
        peer.getDocument addDocumentListener new DocumentListener {
            def changedUpdate(e: DocumentEvent) {
                updateResourceTable
            }
            def insertUpdate(e: DocumentEvent) {
                updateResourceTable
            }
            def removeUpdate(e: DocumentEvent) {
                updateResourceTable
            }
        }
        listenTo(keys)
        reactions += {
            case KeyPressed(_, Key.Up, _, _) =>
                jumpToPreviousResource
                
            case KeyPressed(_, Key.Down, _, _) =>
                jumpToNextResource
        }
    }
    
    private lazy val buttonPanel: Panel = new FlowPanel {        
        contents += Button("Refresh repositories") {
            progressView.showForOperation("Refreshing repositories") {
                refreshRepositories
            }
        }
        
        contents += installButton
        contents += uninstallButton
        contents += refreshButton
        contents += refreshAllBundlesButton
    }
    
    private lazy val refreshButton: Button = Button("Refresh") {
        refreshSelectedBundleResource
    }
    
    private lazy val installButton: Button = Button("Install") {
        installSelectedBundleResource
    }
    
    private lazy val uninstallButton: Button = Button("Uninstall") {
        uninstallSelectedBundleResource
    }
    
    private lazy val refreshAllBundlesButton = Button("Refresh all bundles") {
        refreshAllBundles
    }
    
    def refreshAllBundles
    
    private var resources: Seq[Resource] = Nil
        
    private lazy val progressView: ProgressView = new ProgressView(host = Some(frame), dialogTitle = progressViewTitle)
    
    
    
    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName)
    
    private lazy val frame = new MainFrame {
        title = ResourceManagerView.this.title
        iconImage = icon
        preferredSize = new Dimension(800, 500)
        val screenSize = Toolkit.getDefaultToolkit.getScreenSize 
        
        location = new Point(
            (screenSize.getWidth / 2 - preferredSize.getWidth / 2).asInstanceOf[Int], 
            (screenSize.getHeight / 2 - preferredSize.getHeight / 2).asInstanceOf[Int]
        )
        
        contents = new MigPanel("wrap 1", "[grow,fill]", "[fill][grow,fill][fill]") {
            this add resourceTextField
            this add new ScrollPane {
                viewportView = resourceTable
            }
            this add buttonPanel
        }
        override def closeOperation { 
            hide
        }
    }
     
    
    def installResource(Resource: Resource)
    
    def findResources(wildcard: String): Seq[Resource]
    
    def uninstallResource(Resource: Resource)
    
    def refreshResource(Resource: Resource)
    
    def refreshRepositories
    
    /**
     * Refreshes the resource selected in the table.
     */
    def refreshSelectedResource {
        progressView.showForOperation("Refreshing resource") {
            selectedResource.foreach(refreshResource)
        }
    }
    
    /**
     * Uninstalls the resource selected in the table.
     */
    private def uninstallSelectedResource {
        selectedResource.foreach(uninstallResource)
        update
    }
    
    /**
     * Installs the resource selected in the table.
     */
    private def installSelectedResource {
        progressView.showForOperation("Installing resource") {
            selectedResource.foreach(installResource)
            update
        }
    }
    
    /**
     * Shows or hides buttons depending on which bundle resource is selected.
     */
    private def updateButtons {
        selectedResource match {
            case Some(resource) =>
                import resource._
                installButton.visible = !isInstalled
                uninstallButton.visible = isInstalled
                refreshButton.visible = isInstalled
                
            case None =>
                installButton.visible = false
                uninstallButton.visible = false
                refreshButton.visible = false
        }
        buttonPanel.revalidate
    }
    
    /**
     * Does a complete refresh.
     */
    def update {
        Swing.onEDT {
            updateResourceTable
            updateButtons
        }
    }
    
    /**
     * Selects the next bundle resource in the table.
     */
    private def jumpToNextResource {
        val newIndex = selectedIndex match {
            case Some(i) => i + 1
            case None => 0
        }
        tryToSelectRow(newIndex)
        resourceTable.requestFocus
    }
    
    /**
     * Selects the previous bundle resource in the table.
     */
    private def jumpToPreviousResource {
        val newIndex = selectedIndex match {
            case Some(i) => i - 1
            case None => resources.size - 1
        }
        tryToSelectRow(newIndex)
        resourceTable.requestFocus
    }
    
    /**
     * Tries to select the row at the given position in the table.
     */
    private def tryToSelectRow(rowIndex: Int) {
        if (rowIndex < resources.size) {
            resourceTable.selection.rows.clear
            resourceTable.selection.rows += rowIndex
        }
    }
    
    /**
     * Tries to select the given bundle resource in the table.
     */
    private def tryToSelect(resource: Resource) {
        val rowIndex = resources.indexOf(resource)
        if (rowIndex >= 0) {
            tryToSelectRow(rowIndex)
        }
    }
    
    
    /**
     * Returns the selected bundle resource
     */
    private def selectedResource = {
        selectedIndex.map(resources(_))
    }
    
    /**
     * Returns the selected index
     */
    private def selectedIndex = {
        val selectedIndices = resourceTable.selection.rows
        if (selectedIndices.size > 0 && selectedIndices.head < resources.size) {
            Some(selectedIndices.head)
        } else {
            None
        }
    }
    
    /**
     * Does the search and refreshes the table content. It tries to keep the current selection.
     */
    private def updateResourceTable {
        val previouslySelectedResource = selectedResource
        resources = findResources(resourceTextField.text)
        resourceTableModel.updateData
        resourceTable.revalidate
        previouslySelectedBundleResource.foreach(tryToSelect)
    }
    
    /**
     * Updates the repository refreshing progress view for showing the given repository.
     */
    def notifyRefreshingRepository(repo: Repository) {
        progressView.updateDescription(repo.getURL.toString)
    }
    
    /**
     * Raises the repository refreshing progress bar to reflect the given progress.
     */
    def updateRepositoryRefreshingProgress(progress: Int) {
        progressView.updateProgress(progress)
    }
    
    def show {
        Swing.onEDT {
            /* Load frame */
            frame
            updateButtons
            updateBundleResourceTable
            bundleResourceTextField.requestFocus
            frame.visible = true
        }
    }
    
    def hide {
        Swing.onEDT {
            frame.visible = false
        }
    }
    
    def dispose {
        Swing.onEDT {
            frame.dispose
        }
    }

}
